package com.tencent.sr.iris.activity.service.event.job;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.google.common.base.Strings;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityTaskDTO;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityEventTypeEnum;
import com.tencent.sr.iris.activity.interfaces.event.dto.ActivityEventDTO;
import com.tencent.sr.iris.activity.interfaces.request.task.EventRetryRequest;
import com.tencent.sr.iris.activity.repository.entity.TIrisEventHandingRecordDO;
import com.tencent.sr.iris.activity.service.activity.IrisActivityService;
import com.tencent.sr.iris.activity.service.activity.IrisActivityTaskService;
import com.tencent.sr.iris.activity.service.activity.IrisEventHandingRecordService;
import com.tencent.sr.iris.activity.service.activity.IrisUserActivityRecordService;
import com.tencent.sr.iris.activity.service.event.EventExecutorContext;
import com.tencent.sr.iris.activity.service.event.activity.IrisActivityExecutor;
import com.tencent.sr.iris.activity.service.event.handler.IrisEventHandler;
import com.tencent.sr.iris.activity.service.event.task.IrisTaskExecutor;
import com.tencent.sr.iris.activity.service.event.util.ActivityCommonUtilService;
import com.tencent.sr.iris.activity.service.event.vo.TaskExecutorExtendInfo;
import com.tencent.sr.rmall.common.exception.retail.TRetailErrorException;
import com.tencent.sr.rmall.springbootstarter.elasticjob.TaskJob;
import com.tencent.sr.rmall.springbootstarter.monitor.util.MonitorUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 活动任务事件处理失败重试job，每半个小时执行一次
 *
 * @author xiaojun
 */
@Slf4j
@Component
//@TaskJob(jobName = "activityTaskEventRetryJob", cron = "0 */3 * * * ?", description = "活动任务事件处理失败重试")
@TaskJob(jobName = "activityTaskEventRetryJob", cron = "0 */30 * * * ?", description = "活动任务事件处理失败重试")
public class ActivityTaskEventRetryJob implements SimpleJob {

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private IrisActivityService irisActivityService;
    @Resource
    private IrisActivityTaskService irisActivityTaskService;
    @Resource
    private EventExecutorContext eventExecutorContext;
    @Resource
    private IrisEventHandingRecordService irisEventHandingRecordService;

    @Resource
    private IrisUserActivityRecordService activityRecordService;

    @Resource
    private ActivityCommonUtilService activityCommonUtilService;

    private static final int MAX_SHARDING_ID = 1023;
    /**
     * 分布式锁最大执行时间20分钟
     */
    private static final int MAX_LOCK_TIME = 20;

    /**
     * 分页大小
     */
    private static final int LIMIT = 500;

    @Override
    public void execute(ShardingContext shardingContext) {
        log.info("事件重试任务：重试失败和超时流水start");
        long startTime = System.currentTimeMillis();
        Throwable throwable = null;
        RLock lock = null;
        try {
            lock = redissonClient.getLock(getLockKey());
            if (!lock.tryLock(MAX_LOCK_TIME, TimeUnit.MINUTES)) {
                log.info("事件重试任务：有任务正在执行中，本次忽略扫表");
                return;
            }

            for (int i = 0; i <= MAX_SHARDING_ID; i++) {

                //记录每个shard最大循环次数
                int loopTimes = 0;
                int offset = 0;
                List<TIrisEventHandingRecordDO> failEventRecords = null;
                do {
                    loopTimes++;
                    failEventRecords = irisEventHandingRecordService.queryNeedRetryRecords(i, offset, LIMIT);

                    if (CollectionUtil.isNotEmpty(failEventRecords)) {
                        failEventRecords.stream().forEach(failRecord -> doRetry(failRecord));
                    }
                    log.info("事件重试任务：重试失败和超时流水shardingId:{},总数:{}", i, failEventRecords.size());
                    offset += LIMIT;
                } while (CollectionUtil.isNotEmpty(failEventRecords) && failEventRecords.size() == LIMIT
                        && loopTimes < 10);
            }
        } catch (Exception e) {
            log.error("事件重试任务：执行异常", e);
            throwable = e;
        } finally {
            if (lock != null) {
                lock.unlock();
            }
            log.info("事件重试任务：重试失败和超时流水end");
            MonitorUtil.getSingleMonitorService().logTransaction("activity_job", "activityTaskEventRetryJob",
                    startTime, throwable);
        }
    }



    public void doRetry(TIrisEventHandingRecordDO failRecord) {
        String uid = failRecord.getUid();
        try {
            String eventInfoJson = failRecord.getEventInfo();
            ActivityEventDTO eventDTO = JSON.parseObject(eventInfoJson, ActivityEventDTO.class);
            String idempotent = eventDTO.getIdempotent();
            if (Strings.isNullOrEmpty(idempotent)) {
                log.info("营销事件重试：忽略垃圾数据,eventDTO:{}", JSON.toJSONString(eventDTO));
                return;
            }
            String eventType = failRecord.getEventType();
            String activityBizId = failRecord.getActivityId();
            String taskBizId = failRecord.getTaskId();

            //任务维度失败，重试任务
            if (!StringUtils.isEmpty(taskBizId)) {
                ActivityDTO activityDTO = irisActivityService.getActivity(activityBizId);
                ActivityTaskDTO taskDTO = irisActivityTaskService.getTaskByTaskBizId(taskBizId);
                IrisTaskExecutor taskHandler = eventExecutorContext.getTaskHandler(taskBizId);
                if (taskHandler == null) {
                    log.error("营销事件重试：找不到对应的IrisTaskExecutor uid:{}, taskBizId:{}, idempotent:{}",
                            uid, taskBizId, idempotent);
                    return;
                }

                RLock rLock = null;
                try {
                    rLock = activityCommonUtilService.inviteGiftTryLock(failRecord.getActivityId(), failRecord.getPid(), failRecord.getUid());

                    TaskExecutorExtendInfo extendInfo = TaskExecutorExtendInfo.builder().inviteGiftCheckGmv(true).build();
                    taskHandler.execute(eventDTO, activityDTO, taskDTO, extendInfo);
                    log.info("营销事件重试:task：发起成功 uid:{}, handingRecord bizId:{} ,taskBizId:{}, idempotent:{}",
                            uid, failRecord.getBizId(), taskBizId, idempotent);
                    activityRecordService.addOrUpdateActivityRecord(uid, eventDTO.getEventTime(), activityDTO.getBizId(),
                            activityDTO.getPeriodType(), activityDTO.getPeriod());
                    return;
                } catch (Exception e) {
                    log.error("邀请有礼事件重试失败：", e);
                    throw new TRetailErrorException(e);
                } finally {
                    if (rLock != null && rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                        rLock.unlock();
                    }
                }
            }
            //活动维度失败，重试活动
            if (!StringUtils.isEmpty(activityBizId)) {
                ActivityDTO activityDTO = irisActivityService.getActivity(activityBizId);
                IrisActivityExecutor activityExecutor = eventExecutorContext.getActivityHandler(
                        activityBizId);
                if (activityExecutor == null) {
                    log.error("营销事件重试:activity：找不到对应的IrisActivityExecutor uid:{} ,activityBizId:{}, idempotent:{}",
                            uid, activityBizId, idempotent);
                    return;
                }
                activityExecutor.execute(eventDTO, activityDTO);
                log.info("营销事件重试:event：发起成功 uid:{},handingRecord bizId:{} ,activityBizId:{}, idempotent:{}",
                        uid, failRecord.getBizId(), activityBizId, idempotent);
                return;
            }
            //事件维度失败，重试事件
            if (!StringUtils.isEmpty(eventType)) {
                IrisEventHandler eventHandler = eventExecutorContext.getEventHandler(
                        ActivityEventTypeEnum.getByCode(eventType));
                if (eventHandler == null) {
                    log.error("营销事件重试:event：找不到对应的IrisEventHandler uid:{}, eventType:{}, idempotent:{}", uid, eventType, idempotent);
                    return;
                }
                eventHandler.execute(eventDTO);
                log.info("营销事件重试:event：发起成功  uid:{},handingRecord bizId:{},idempotent:{}"
                        , uid, failRecord.getBizId(), idempotent);
            }

        } catch (Exception e) {
            log.error("营销事件重试：发起重试失败和超时流水失败 uid:{}, bizId:{}", uid, failRecord.getBizId(), e);
        }
    }

    private String getLockKey() {
        return "activityTaskEventRetryJob_lock";
    }

    /**
     * 事件手动重试入口
     *
     * @param request
     */
    public void execute(EventRetryRequest request) {
        log.info("营销手动事件重试：重试失败和超时流水start,request:{}", JSON.toJSONString(request));
        long startTime = System.currentTimeMillis();
        Throwable throwable = null;
        RLock lock = null;
        try {
            lock = redissonClient.getLock(getLockKey());
            if (!lock.tryLock(MAX_LOCK_TIME, TimeUnit.MINUTES)) {
                log.info("营销手动事件重试：有任务正在执行中，本次忽略扫表");
                return;
            }

            for (int i = 0; i <= MAX_SHARDING_ID; i++) {

                //记录每个shard最大循环次数
                int loopTimes = 0;
                int offset = 0;
                List<TIrisEventHandingRecordDO> handingRecords = null;
                do {
                    loopTimes++;
                    handingRecords = irisEventHandingRecordService.queryHandingRecords(i, offset, LIMIT, request);
                    if (CollectionUtil.isNotEmpty(handingRecords)) {
                        handingRecords.stream().forEach(failRecord -> doRetry(failRecord));
                    }
                    log.info("营销手动事件重试：重试失败和超时流水shardingId:{},总数:{}", i, handingRecords.size());
                    offset += LIMIT;
                } while (CollectionUtil.isNotEmpty(handingRecords) && handingRecords.size() == LIMIT
                        && loopTimes < 10);
            }
        } catch (Exception e) {
            log.error("营销手动事件重试：执行异常,request:{}", JSON.toJSONString(request), e);
            throwable = e;
        } finally {
            if (lock != null) {
                lock.unlock();
            }
            log.info("营销手动事件重试：重试失败和超时流水end");
            MonitorUtil.getSingleMonitorService().logTransaction("activity_job", "activityTaskEventRetryJob",
                    startTime, throwable);
        }
    }


}
